<?php
namespace app\api\library\curd;

use Exception;

class BuildCurd
{

    /**
     * api的crud文件目录
     * @var string
     */
    protected $apiRootPath = "api";

    /**
     * 分隔符
     */
    protected $DS = DIRECTORY_SEPARATOR;

    /**
     * 应用目录
     * @var string
     */
    protected $rootDir;

    /**
     * 主表
     * @var string
     */
    protected $table;

    /**
     * 生成的控制器名
     * @var string
     */
    protected $controllerFilename;

    /**
     * 控制器命名空间
     * @var string
     */
    protected $controllerNamespace;

    /**
     * 控制器命名
     * @var string
     */
    protected $controllerName;

    /**
     * 生成的模型文件名
     * @var string
     */
    protected $modelFilename;

    /**
     * 主表模型命名
     * @var string
     */
    protected $modelName;

    /**
     * 验证器命名空间
     * @var string
     */
    protected $validateNamespace;

    /**
     * 验证器文件名
     * @var string
     */
    protected $validateFilename;

    /**
     * 验证器名
     * @var string
     */
    protected $validateName;

    /**
     * 相关生成文件
     * @var array
     */
    protected $fileList = [];

    public function __construct()
    {
        $this->rootDir = APP_PATH;
    }

    /**
     * 设置主表
     * @param $table
     * @return $this
     * @throws TableException
     */
    public function setTable($table)
    {
        $this->table = $table;

        // 初始化默认控制器名
        $this->setControllerFilename($this->table);

        // 初始化默认模型名
        $this->setModelFilename($this->table);

        //初始化默认的验证器
        $this->setValidateFilename($this->table);

        $this->setData();

        return $this;
    }

    /**
     * 设置控制器名称、命名空间、模型名称
     * @return $this
     */
    protected function setData()
    {
        $nodeArray = explode($this->DS, str_replace('/', $this->DS, $this->lineToHump($this->table)));

        // 控制器命名空间
        $namespaceArray            = $nodeArray;
        $this->controllerName      = ucfirst(array_pop($namespaceArray));
        $namespaceSuffix           = implode('\\', $namespaceArray);
        $this->controllerNamespace = empty($namespaceSuffix) ? "app\\{$this->apiRootPath}\\controller" : "app\\{$this->apiRootPath}\\\controller\\{$namespaceSuffix}";

        // 主表模型命名
        $modelArray = explode($this->DS, $this->modelFilename);

        $this->modelName = array_pop($modelArray);

        //验证器命名空间
        $this->validateNamespace = empty($namespaceSuffix) ? "app\\{$this->apiRootPath}\\validate" : "app\\{$this->apiRootPath}\\validate\\{$namespaceSuffix}";

        return $this;
    }

    /**
     * 设置控制器名
     * @param $controllerFilename
     * @return $this
     */
    public function setControllerFilename($controllerFilename)
    {
        $this->controllerFilename = str_replace('/', $this->DS, $controllerFilename);
        $arr = explode($this->DS, $this->controllerFilename);
        $end = end($arr);
        $this->controllerFilename = str_replace($end, ucfirst($this->lineToHump($end)), $this->controllerFilename);

        return $this;
    }

    /**
     * 设置模型名
     * @param $modelFilename
     * @return $this
     */
    public function setModelFilename($modelFilename)
    {
        $this->modelFilename = str_replace('/', $this->DS, $modelFilename);
        $arr = explode($this->DS, $this->modelFilename);
        $end = end($arr);
        $this->modelFilename = str_replace($end, ucfirst($this->lineToHump($end)), $this->modelFilename);

        return $this;
    }

    /**
     * 设置验证器名
     * @param $validateFilename
     * @return $this
     */
    public function setValidateFilename($validateFilename)
    {
        $this->validateFilename = str_replace('/', $this->DS, $validateFilename);
        $arr = explode($this->DS, $this->validateFilename);
        $end = end($arr);
        $this->validateFilename = str_replace($end, ucfirst($this->lineToHump($end)), $this->validateFilename);
        return $this;
    }

    /**
     * 初始化
     * @return $this
     */
    public function render()
    {
        // 控制器
        $this->renderController();

        // 模型
        $this->renderModel();

        //验证器
        $this->renderValidate();

        return $this;
    }

    /**
     * 初始化控制器
     * @return $this
     */
    protected function renderController()
    {
        $controllerFile = "{$this->rootDir}{$this->DS}{$this->apiRootPath}{$this->DS}controller{$this->DS}{$this->controllerFilename}.php";

        $extendNamespaceArray = explode($this->DS, $this->controllerFilename);
        $extendNamespace      = null;
        if (count($extendNamespaceArray) > 1) {
            array_pop($extendNamespaceArray);
            $extendNamespace = '\\' . implode('\\', $extendNamespaceArray);
        }
        $modelFilenameExtend = str_replace($this->DS, '\\', $this->modelFilename);
        $controllerValue     = $this->replaceTemplate(
            $this->getTemplate("controller{$this->DS}controller"),
            [
                'controllerName'      => $this->controllerName,
                'controllerNamespace' => "app\\{$this->apiRootPath}\controller{$extendNamespace}",
                'modelFilename'       => "\app\\{$this->apiRootPath}\model\\{$modelFilenameExtend}",
                'methods'             => $this->getTemplate("controller{$this->DS}methods"),
            ]);
        $this->fileList[$controllerFile] = $controllerValue;
        return $this;
    }

    /**
     * 初始化模型
     * @return $this
     */
    protected function renderModel()
    {
        // 主表模型
        $modelFile = "{$this->rootDir}{$this->DS}{$this->apiRootPath}{$this->DS}model{$this->DS}{$this->modelFilename}.php";

        $extendNamespaceArray = explode($this->DS, $this->modelFilename);
        $extendNamespace      = null;
        if (count($extendNamespaceArray) > 1) {
            array_pop($extendNamespaceArray);
            $extendNamespace = '\\' . implode('\\', $extendNamespaceArray);
        }
        $modelValue = $this->replaceTemplate(
            $this->getTemplate("model{$this->DS}model"),
            [
                'modelName'      => $this->modelName,
                'modelNamespace' => "app\\{$this->apiRootPath}\model{$extendNamespace}",
                'table'          => $this->table,
            ]);
        $this->fileList[$modelFile] = $modelValue;

        return $this;
    }

    /**
     * 初始化验证器
     * @return $this
     */
    protected function renderValidate()
    {
        $validateFile = "{$this->rootDir}{$this->DS}{$this->apiRootPath}{$this->DS}validate{$this->DS}{$this->validateFilename}.php";

        $validateValue = $this->replaceTemplate(
            $this->getTemplate("validate{$this->DS}validate"),
            [
                'validateName'      => $this->modelName,
                'validateNamespace' => $this->validateNamespace
            ]);
        $this->fileList[$validateFile] = $validateValue;

        return $this;
    }

    /**
     * 检测文件
     * @return $this
     */
    protected function check()
    {
        foreach ($this->fileList as $key => $val) {
            if (is_file($key)) {
                throw new Exception("文件已存在：{$key}");
            }
        }
        return $this;
    }

    /**
     * 开始生成
     * @return array
     */
    public function create()
    {
        $this->check();
        foreach ($this->fileList as $key => $val) {

            // 判断文件夹是否存在,不存在就创建
            $fileArray = explode($this->DS, $key);
            array_pop($fileArray);
            $fileDir = implode($this->DS, $fileArray);
            if (!is_dir($fileDir)) {
                mkdir($fileDir, 0775, true);
            }

            // 写入
            file_put_contents($key, $val);
        }
        return array_keys($this->fileList);
    }

    /**
     * 获取相关的文件
     * @return array
     */
    public function getFileList()
    {
        return $this->fileList;
    }

    /**
     * 下划线转驼峰
     * @param $str
     * @return null|string|string[]
     */
    protected function lineToHump($str)
    {
        $str = preg_replace_callback('/([-_]+([a-z]{1}))/i', function ($matches) {
            return strtoupper($matches[2]);
        }, $str);
        return $str;
    }

    /**
     * 模板值替换
     * @param $string
     * @param $array
     * @return mixed
     */
    protected function replaceTemplate($string, $array)
    {
        foreach ($array as $key => $val) {
            if (is_null($val)) {
                $val = '';
            }

            $string = str_replace("{{" . $key . "}}", $val, $string);
        }
        return $string;
    }

    /**
     * 获取对应的模板信息
     * @param $name
     * @return false|string
     */
    protected function getTemplate($name)
    {
        return file_get_contents(__DIR__."{$this->DS}templates{$this->DS}{$name}.code");
    }

}
